/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
 * extension by Rain Man  2010-09-29
*
*/
using System;
using System.Xml.Schema;
using System.Qizx.Api;
using System.Qizx.Extensions;
using System.Qizx.Util;
using System.Qizx.Api.Util;
using DataModelException = System.Qizx.Api.DataModelException;
using EvaluationException = System.Qizx.Api.EvaluationException;
using Node = System.Qizx.Api.Node;
using BasicNode = System.Qizx.Xdm.BasicNode;
using XQItem = System.Qizx.XQuery.XQItem;
using XQItemType = System.Qizx.XQuery.XQItemType;
using XQType = System.Qizx.XQuery.XQType;
using XQValue = System.Qizx.XQuery.XQValue;
using System.Qizx.Xdm;
using System.Xml;

namespace System.Qizx.XQuery.Dt {

    /// <summary> Performs atomization on an input sequence. A Node is potentially expanded
    /// into a sequence of atoms, which is inlined. Schema are in fact not
    /// implemented, so we take the string-value of the node.
    /// </summary>
    public class Atomizer : GenericValue {
        internal XQValue input;

        public Atomizer(XQValue value) {
            this.input = value;
        }

        public override bool Next() {
            if (!input.Next())
                return false;
            // Too simple when using Schema: can be a sequence to inline
            item = ToSingleAtom(input.Item); // TODO SCHEMA
            return true;
        }



        // Simplified version, sufficient in absence of Schema
        public static XQItem ToSingleAtom(XQItem item) {
            if (!item.IsNode)
                return item;

            return ToSingleAtom(item.GetNode());
        }

        public static XQItem ToSingleAtom(XmlNode node, XmlTypeCode typeCode) {
            string value = node.InnerText;
            try {
                //HACK CoShare
                switch (typeCode) {
                    case XmlTypeCode.Byte:
                        return new SingleInteger(int.Parse(value), XQItemType.BYTE);
                    case XmlTypeCode.Int:
                        return new SingleInteger(int.Parse(value), XQItemType.INT);
                    case XmlTypeCode.Integer:
                        return new SingleInteger(int.Parse(value));
                    case XmlTypeCode.Long:
                        return new SingleInteger(int.Parse(value), XQItemType.LONG);
                    case XmlTypeCode.NegativeInteger:
                        return new SingleInteger(int.Parse(value), XQItemType.NEGATIVE_INTEGER);
                    case XmlTypeCode.NonNegativeInteger:
                        return new SingleInteger(int.Parse(value), XQItemType.NON_NEGATIVE_INTEGER);
                    case XmlTypeCode.NonPositiveInteger:
                        return new SingleInteger(int.Parse(value), XQItemType.NON_POSITIVE_INTEGER);
                    case XmlTypeCode.PositiveInteger:
                        return new SingleInteger(int.Parse(value), XQItemType.POSITIVE_INTEGER);
                    case XmlTypeCode.Short:
                        return new SingleInteger(int.Parse(value), XQItemType.SHORT);
                    case XmlTypeCode.UnsignedByte:
                        return new SingleInteger(int.Parse(value), XQItemType.UNSIGNED_BYTE);
                    case XmlTypeCode.UnsignedInt:
                        return new SingleInteger(int.Parse(value), XQItemType.UNSIGNED_INT);
                    case XmlTypeCode.UnsignedLong:
                        return new SingleInteger(int.Parse(value), XQItemType.UNSIGNED_LONG);
                    case XmlTypeCode.UnsignedShort:
                        return new SingleInteger(int.Parse(value), XQItemType.UNSIGNED_SHORT);
                    case XmlTypeCode.Decimal:
                        return new SingleDecimal(decimal.Parse(value));
                    case XmlTypeCode.Double:
                        return new SingleDouble(double.Parse(value));
                    case XmlTypeCode.Float:
                        return new SingleFloat(float.Parse(value));
                    case XmlTypeCode.Boolean:
                        return new SingleBoolean(value == "true" || value == "1");

                    case XmlTypeCode.Date:
                        return new SingleMoment(XQDate.parseDate(value.Trim()), XQItemType.DATE);
                    case XmlTypeCode.DateTime:
                        return new SingleMoment(XQDateTime.ParseDateTime(value.Trim()), XQItemType.DATE_TIME);
                    case XmlTypeCode.Time:
                        return new SingleMoment(XQTime.parseTime(value.Trim()), XQItemType.TIME);
                    case XmlTypeCode.Duration:
                        return new SingleDuration(XQDuration.parseDuration(value));
                    case XmlTypeCode.GDay:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.G_DAY);
                    case XmlTypeCode.GMonth:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.G_MONTH);
                    case XmlTypeCode.GMonthDay:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.G_MONTH_DAY);
                    case XmlTypeCode.GYear:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.G_YEAR);
                    case XmlTypeCode.GYearMonth:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.G_YEAR_MONTH);
                    case XmlTypeCode.DayTimeDuration:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.DAY_TIME_DURATION);
                    case XmlTypeCode.YearMonthDuration:
                        return new SingleDuration(XQDuration.parseDuration(value), XQItemType.YEAR_MONTH_DURATION);
                    case XmlTypeCode.AnyUri:
                        return new SingleString(value, XQItemType.ANYURI);
                    case XmlTypeCode.Base64Binary:
                        return new SingleBinary(Binary.parseBase64Binary(value), XQItemType.BASE64_BINARY);
                    case XmlTypeCode.HexBinary:
                        return new SingleBinary(Binary.parseHexBinary(value), XQItemType.BASE64_BINARY);
                    case XmlTypeCode.Language:
                        return new SingleString(value, XQItemType.LANGUAGE);
                    case XmlTypeCode.NCName:
                        return new SingleString(XmlConvert.VerifyNCName(value), XQItemType.NCNAME);
                    case XmlTypeCode.NmToken:
                        return new SingleString(XmlConvert.VerifyNMTOKEN(value), XQItemType.NMTOKEN);
                    case XmlTypeCode.Notation:
                        return new SingleString(XmlConvert.VerifyTOKEN(value), XQItemType.TOKEN);
                    case XmlTypeCode.QName:
                        string prefix, localName;
                        XmlHelper.Split(value, out prefix, out localName);
                        string uri = node.GetNamespaceOfPrefix(prefix);
                        if (uri == null) {
                            uri = string.Empty;
                        }
                        return new SingleQName(IQName.GetQName(uri, localName));
                    case XmlTypeCode.String:
                        return new SingleString(value);
                    case XmlTypeCode.NormalizedString:
                        return new SingleString(value, XQItemType.NORMALIZED_STRING);

                    default:
                        //TODO coshare;
                        break;
                }

            } catch (DataModelException e) {
                //throw WrapDMException(e);
            }
            return new SingleString(value, XQType.STRING);
        }

        public static XQItem ToSingleAtom(Node node) {
            try {
                //HACK CoShare
                IXdmSchemaInfo schemaInfo = node.SchemaInfo;
                if (schemaInfo != null && schemaInfo.DataType != null) {
                    switch (schemaInfo.DataType.TypeCode) {
                        case XmlTypeCode.Byte:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.BYTE);
                        case XmlTypeCode.Int:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.INT);
                        case XmlTypeCode.Integer:
                            return new SingleInteger(node.ValueAsInteger);
                        case XmlTypeCode.Long:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.LONG);
                        case XmlTypeCode.NegativeInteger:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.NEGATIVE_INTEGER);
                        case XmlTypeCode.NonNegativeInteger:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.NON_NEGATIVE_INTEGER);
                        case XmlTypeCode.NonPositiveInteger:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.NON_POSITIVE_INTEGER);
                        case XmlTypeCode.PositiveInteger:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.POSITIVE_INTEGER);
                        case XmlTypeCode.Short:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.SHORT);
                        case XmlTypeCode.UnsignedByte:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.UNSIGNED_BYTE);
                        case XmlTypeCode.UnsignedInt:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.UNSIGNED_INT);
                        case XmlTypeCode.UnsignedLong:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.UNSIGNED_LONG);
                        case XmlTypeCode.UnsignedShort:
                            return new SingleInteger(node.ValueAsInteger, XQItemType.UNSIGNED_SHORT);
                        case XmlTypeCode.Decimal:
                            return new SingleDecimal(node.ValueAsDecimal);
                        case XmlTypeCode.Double:
                            return new SingleDouble(node.ValueAsDouble);
                        case XmlTypeCode.Float:
                            return new SingleFloat(node.ValueAsFloat);
                        case XmlTypeCode.Boolean:
                            return new SingleBoolean(node.ValueAsBoolean);

                        case XmlTypeCode.Date:
                            return new SingleMoment(XQDate.parseDate(node.StringValue.Trim()), XQItemType.DATE);
                        case XmlTypeCode.DateTime:
                            return new SingleMoment(XQDateTime.ParseDateTime(node.StringValue.Trim()), XQItemType.DATE_TIME);
                        case XmlTypeCode.Time:
                            return new SingleMoment(XQTime.parseTime(node.StringValue.Trim()), XQItemType.TIME);
                        case XmlTypeCode.Duration:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue));
                        case XmlTypeCode.GDay:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.G_DAY);
                        case XmlTypeCode.GMonth:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.G_MONTH);
                        case XmlTypeCode.GMonthDay:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.G_MONTH_DAY);
                        case XmlTypeCode.GYear:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.G_YEAR);
                        case XmlTypeCode.GYearMonth:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.G_YEAR_MONTH);
                        case XmlTypeCode.DayTimeDuration:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.DAY_TIME_DURATION);
                        case XmlTypeCode.YearMonthDuration:
                            return new SingleDuration(XQDuration.parseDuration(node.StringValue), XQItemType.YEAR_MONTH_DURATION);
                        case XmlTypeCode.AnyUri:
                            return new SingleString(node.ValueAsString, XQItemType.ANYURI);
                        case XmlTypeCode.Base64Binary:
                            return new SingleBinary(Binary.parseBase64Binary(node.ValueAsString), XQItemType.BASE64_BINARY);
                        case XmlTypeCode.HexBinary:
                            return new SingleBinary(Binary.parseHexBinary(node.ValueAsString), XQItemType.BASE64_BINARY);
                        case XmlTypeCode.Language:
                            return new SingleString(node.ValueAsString, XQItemType.LANGUAGE);
                        case XmlTypeCode.NCName:
                            return new SingleString(XmlConvert.VerifyNCName(node.ValueAsString), XQItemType.NCNAME);
                        case XmlTypeCode.NmToken:
                            return new SingleString(XmlConvert.VerifyNMTOKEN(node.ValueAsString), XQItemType.NMTOKEN);
                        case XmlTypeCode.Notation:
                            return new SingleString(XmlConvert.VerifyTOKEN(node.ValueAsString), XQItemType.TOKEN);
                        case XmlTypeCode.QName:
                            return new SingleQName(node.ValueAsQName);
                        case XmlTypeCode.String:
                            return new SingleString(node.ValueAsString);
                        case XmlTypeCode.NormalizedString:
                            return new SingleString(node.ValueAsString, XQItemType.NORMALIZED_STRING);
                        default:
                            NameTable nameTable = new NameTable();
                            object value = schemaInfo.DataType.ParseValue(node.StringValue, nameTable, null);
                            if (value != null) {
                                if (value is DateTime) {
                                    if (node.StringValue.Contains("T")) {
                                        return new SingleMoment(XQDateTime.ParseDateTime(node.StringValue.Trim()), XQItemType.DATE_TIME);
                                    } else {
                                        return new SingleMoment(XQDate.parseDate(node.StringValue.Trim()), XQItemType.DATE);
                                    }
                                }
                            }
                            //TODO coshare;
                            break;
                    }
                }
            } catch (DataModelException e) {
                //throw WrapDMException(e);
            }
            return new SingleString(node.StringValue, XQType.STRING);

        }

        public static XmlSchemaDatatype GetDataType(IXmlSchemaInfo schemaInfo) {
            if (schemaInfo.Validity != XmlSchemaValidity.Valid) return null;
            XmlSchemaType schemaType = schemaInfo.SchemaType;
            if (schemaType != null) {
                if (schemaType.Datatype != null)
                    return schemaType.Datatype;
            }
            //TODO Rain Man
            return null;
        }

        public override XQValue BornAgain() {
            return new Atomizer(input.BornAgain());
        }
    }
}